#ifndef __language_H__
#define __language_H__

#ifdef _WIN32
#pragma warning( disable:4786 )
#endif

#include "syntax_entry.h"
#include "parse_info.h"
#include "callstack.h"

#include <time.h>

#include <iostream>
#include <map>
#include <string>
using namespace std;

namespace syntax_analysis
{
	class language
	{
	public:
		language()
		{
			m_name = "";
			m_entry = "";
			m_syntax.clear();
		}

		language( string name, string entry, const multimap<string,syntax_entry*> &syn )
		{
			m_name = name;
			m_entry = entry;
			m_syntax = syn;
		}

		virtual ~language()
		{
			multimap<string,syntax_entry*>::iterator it;
			for( it = m_syntax.begin(); it != m_syntax.end(); ++it )
				delete it->second;
			m_name = "";
			m_entry = "";
			m_syntax.clear();

		}

		string to_string()
		{
			multimap<string,syntax_entry*>::iterator i;
			string lang;

			lang += "language=\"";
			lang += m_name;
			lang += "\"\nentry=\"";
			lang += m_entry;
			lang += "\"\n";
			
			for( i = m_syntax.begin(); i != m_syntax.end(); ++i )
			{
				lang += i->second->to_string();
				lang += "\n";
			}
			return lang;
		}

	public:
		string get_name()
		{
			return m_name;
		}

		void set_name( string name )
		{
			m_name = name;
		}

		string get_entry()
		{
			return m_entry;
		}

		void set_entry( string entry )
		{
			m_entry = entry;
		}

		const multimap<string,syntax_entry*>& get_syntax()
		{
			return m_syntax;
		}

		syntax_entry* get_syntax( string name, int n )
		{
			syntax_entry *syn = NULL;
			multimap<string,syntax_entry*>::iterator i;

			i = m_syntax.find( name );
			advance( i, n );
			if( i != m_syntax.end() && i->first == name )
				syn = i->second;
	
			return syn;
		}

		void set_syntax( const multimap<string,syntax_entry*> &syn )
		{
			m_syntax = syn;
		}

		void add_syntax( syntax_entry *syn )
		{
			m_syntax.insert( pair<string,syntax_entry*>( syn->get_name(), syn ) );
		}

		bool check_integrity()
		{
			multimap<string,syntax_entry*>::iterator i;

			if( m_syntax.find( m_entry ) == m_syntax.end() )
			{
				printf( "syntax entry not found.\n" );
				return false;
			}

			for( i = m_syntax.begin(); i != m_syntax.end(); ++i )
			{
				int pos = 0;
				syntax *syn;
				syntax_compound *compound;
				syntax_entry *entry;
				entry = i->second;
				compound = entry->get_body();
				do 
				{
					syn = compound->get_child( pos );
					if( syn != NULL && syn->get_type() == SYNTAX_CALL )
					{
						string name;
						name = ((syntax_call*)syn)->get_name();
						if( m_syntax.find( name ) == m_syntax.end() )
						{
							printf( "syntax %s not found.\n", name.c_str() );
							return false;
						}
					}
					++pos;
				} while( syn != NULL );
			}
			return true;
		}

		bool parse( char *code )
		{
			syntax *syn = NULL;
			parse_info *info;
			parse_para *para;
			bool result = false;
			int len = strlen( code );

			clear_error( code );

			syn = get_syntax( m_entry, 0 );
			if( syn != NULL )
			{
				para = new parse_para();
				info = new parse_info();
				para->set_code( code );
				para->set_language( this );
				info->set_parse_para( para );
				para = NULL;

				m_info = info;
				time_t start_time = time( NULL );
				syn->init_called();
				do
				{
					if( result )
						result = syn->reparse( info );
					else
						result = syn->parse( info );
					para = info->get_parse_para();

				} while( result && para->get_acceptted( para->get_acceptted_count() - 1 ) != len );

#ifdef _DEBUG_
				cout << time(NULL) - start_time << "s used." << endl;
				cout << syn->get_called() << " called." << endl;
				cout << syn->get_called() / strlen( code ) << " performance." << endl;
#endif
				delete info;
	
				if( !result )
					report_error();
			}
			return result;
		}

		void clear_error( char *code )
		{
			m_error_from_code = code;
			m_error_from_stack.clear();
		}
		void set_error( const callstack &from, char *code )
		{
			if( code > m_error_from_code )
			{
				m_error_from_stack.clear();
				m_error_from_code = code;
				m_error_from_stack.push_back( from );
			}
			else if( code == m_error_from_code )
			{
				syntax *syn = from.top();
				if( syn != NULL && syn->get_type() == SYNTAX_CHAR )
				{
					m_error_from_stack.clear();
					m_error_from_code = code;
					m_error_from_stack.push_back( from );
				}
			}
		}

		void report_error()
		{
			if( m_error_from_stack.size() > 0 )
			{
				cout << "error caused by:" << endl;
				list<callstack>::iterator i;
				for( i = m_error_from_stack.begin(); i != m_error_from_stack.end(); ++i )
				{
					callstack::iterator it;
					callstack st;
					st = *i;
					int n = 0;
					for( it = st.begin(); it != st.end(); ++it, ++n )
					{
						int m;
						for( m = 0; m < n; ++m )
							cout << "  ";
						cout << (*it)->to_string() << endl;
					}
				}
				cout << "from code:" << endl;
				char buf[128];
				strncpy( buf, m_error_from_code, 127 );
				buf[127] = 0;
				cout << buf << endl;
			}
		}

		callstack& get_callstack()
		{
			return m_callstack;
		}

		void print_stack()
		{
			callstack::iterator it;
			int n = 0;
			for( it = m_callstack.begin(); it != m_callstack.end(); ++it, ++n )
			{
				int m;
				for( m = 0; m < n; ++m )
					cout << "  ";
				cout << (*it)->to_string() << endl;
			}
		}

		void print_parse_info()
		{
			m_info->print( 0 );
		}

	private:
		list<callstack> m_error_from_stack;
		char *m_error_from_code;
		string m_name;
		string m_entry;
		multimap<string,syntax_entry*> m_syntax;
		callstack m_callstack;
		parse_info *m_info;
	};
}

#endif // __language_h__
